#include "stdafx.h"
#include "qvideoplayer.h"

#include "../qcom.h"
using namespace micro::com;


#pragma comment(lib,"mf.lib")
#pragma comment(lib,"mfplat.lib")
#pragma comment(lib,"mfuuid.lib")
#pragma comment(lib,"strmiids.lib")

using namespace micro::media;



//////////////////////////////////////////////////////////////////////////

HRESULT CreateSourceStreamNode(
    IMFMediaSource * pSource,
    IMFPresentationDescriptor * pSourcePD,
    IMFStreamDescriptor * pSourceSD,
    IMFTopologyNode ** ppNode
);

HRESULT CreateOutputNode(
    IMFStreamDescriptor * pSourceSD,
    HWND hwndVideo,
    IMFTopologyNode ** ppNode
);


//  Static class method to create the QVideoPlayer object.
//
//  hVideo:   Handle to the video window.
//  hEvent:   Handle to the window to receive notifications.
//  ppPlayer: Receives an AddRef's pointer to the QVideoPlayer object.
//            The caller must release the pointer.

HRESULT QVideoPlayer::CreateInstance( HWND hVideo, HWND hEvent, QVideoPlayer ** ppPlayer )
{
    ASSERT( hVideo != nullptr );
    ASSERT( hEvent != nullptr );

    if ( ppPlayer == nullptr )
    {
        return E_POINTER;
    }

    HRESULT hr = S_OK;

    QVideoPlayer * pPlayer = new QVideoPlayer( hVideo, hEvent );

    if ( pPlayer == nullptr )
    {
        return E_OUTOFMEMORY;
    }

    hr = pPlayer->Initialize();

    if ( SUCCEEDED( hr ) )
    {
        *ppPlayer = pPlayer;
        ( *ppPlayer )->AddRef();
    }

    // The QVideoPlayer constructor sets the ref count to 1.
    // If the method succeeds, the caller receives an AddRef'd pointer.
    // Whether the method succeeds or fails, we must release the pointer.

    SafeRelease( &pPlayer );

    return hr;
}


QVideoPlayer::QVideoPlayer( HWND hVideo, HWND hEvent ) :
    m_pSession( nullptr ),
    m_pSource( nullptr ),
    m_pVideoDisplay( nullptr ),
    m_hwndVideo( hVideo ),
    m_hwndEvent( hEvent ),
    m_state( Closed ),
    m_hCloseEvent( nullptr ),

    ///audio
    m_pAudioVolum( nullptr ),
    m_fLevel( 0.5f ), //50% volume
    m_bMute( FALSE ),

    m_nRefCount( 1 )
{

}

QVideoPlayer::~QVideoPlayer()
{
    ASSERT( m_pSession == nullptr ); // If FALSE, the app did not call Shutdown().

    // The application must call Shutdown because the media session holds a
    // reference count on the QVideoPlayer object. (This happens when QVideoPlayer calls
    // IMediaEventGenerator::BeginGetEvent on the media session.) As a result,
    // there is a circular reference count between the QVideoPlayer object and the
    // media session. Calling Shutdown breaks the circular reference count.

    // If CreateInstance failed, the application will not call Shutdown. To
    // handle that case, we must call Shutdown() in the destructor. The
    // circular ref-count problem does not occcur if CreateInstance has failed.
    // Also, calling Shutdown twice is harmless.

    Shutdown();
}



//  Initializes the QVideoPlayer object.

HRESULT QVideoPlayer::Initialize()
{
    if ( m_hCloseEvent )
    {
        return MF_E_ALREADY_INITIALIZED;
    }

    HRESULT hr = S_OK;

    //mutat in myapp
    //// Start up Media Foundation platform.
    //HRESULT hr = MFStartup( MF_VERSION, MFSTARTUP_LITE );
    //
    //if ( FAILED( hr ) )
    //{
    //    return hr;
    //}




    m_hCloseEvent = CreateEvent( nullptr, FALSE, FALSE, nullptr );

    if ( m_hCloseEvent == nullptr )
    {
        hr = HRESULT_FROM_WIN32( GetLastError() );
    }

    return hr;
}


//  Releases all resources held by this object.


HRESULT QVideoPlayer::Shutdown()
{
    HRESULT hr = S_OK;

    // Close the session
    hr = CloseSession();

    //mutat in MyApp
    ///MFShutdown();



    if ( m_hCloseEvent )
    {
        CloseHandle( m_hCloseEvent );
        m_hCloseEvent = nullptr;
    }

    return hr;
}



// IUnknown methods

HRESULT QVideoPlayer::QueryInterface( REFIID riid, void ** ppv )
{
    static const QITAB qit[] =
    {
        QITABENT( QVideoPlayer, IMFAsyncCallback ),
        { 0 },
    };
    return QISearch( this, qit, riid, ppv );
}

ULONG QVideoPlayer::AddRef()
{
    return InterlockedIncrement( &m_nRefCount );
}

ULONG QVideoPlayer::Release()
{
    ULONG uCount = InterlockedDecrement( &m_nRefCount );

    if ( uCount == 0 )
    {
        delete this;
    }

    return uCount;
}


// Opens a URL for playback.

HRESULT QVideoPlayer::OpenURL( PCWSTR sURL )
{
    // 1. Create a new media session.
    // 2. Create the media source.
    // 3. Create the topology.
    // 4. Queue the topology [asynchronous]
    // 5. Start playback [asynchronous - does not happen in this method.]

    IMFTopology * pTopology = nullptr;

    // Create the media session.
    HRESULT hr = CreateSession();

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Create the media source.
    hr = CreateMediaSource( sURL );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Create a partial topology.
    hr = CreateTopologyFromSource( &pTopology );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Set the topology on the media session.
    hr = m_pSession->SetTopology( 0, pTopology );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Set our state to "open pending"
    m_state = OpenPending;

    // If SetTopology succeeds, the media session will queue an
    // MESessionTopologySet event.

done:

    if ( FAILED( hr ) )
    {
        m_state = Closed;
    }

    SafeRelease( &pTopology );
    return hr;
}


//  Starts playback from paused or stopped state.

HRESULT QVideoPlayer::Play()
{
    if ( m_state != Paused && m_state != Stopped )
    {
        return MF_E_INVALIDREQUEST;
    }

    if ( m_pSession == nullptr || m_pSource == nullptr )
    {
        return E_UNEXPECTED;
    }

    HRESULT hr = StartPlayback();

    return hr;
}


//  Pauses playback.

HRESULT QVideoPlayer::Pause()
{
    if ( m_state != Started )
    {
        return MF_E_INVALIDREQUEST;
    }

    if ( m_pSession == nullptr || m_pSource == nullptr )
    {
        return E_UNEXPECTED;
    }

    HRESULT hr = m_pSession->Pause();

    if ( SUCCEEDED( hr ) )
    {
        m_state = Paused;
    }

    return hr;
}

//  Repaints the video window.
//
//  The application calls this method when it receives a WM_PAINT message.

HRESULT QVideoPlayer::Repaint()
{
    HRESULT hr = S_OK;

    if ( m_pVideoDisplay )
    {
        hr = m_pVideoDisplay->RepaintVideo();
    }

    return hr;
}


//  Resizes the video rectangle.
//
//  The application calls this method if the size of the video window changes;
//  e.g., when the application receives a WM_SIZE message.

HRESULT QVideoPlayer::ResizeVideo( WORD width, WORD height )
{
    HRESULT hr = S_OK;

    if ( m_pVideoDisplay )
    {
        // Set the destination rectangle.
        // Leave the default source rectangle (0,0,1,1).

        RECT rcDest = { 0, 0, width, height };

        hr = m_pVideoDisplay->SetVideoPosition( nullptr, &rcDest );
    }

    return hr;
}


//  Callback for asynchronous BeginGetEvent method.
//
//  pAsyncResult: Pointer to the result.

HRESULT QVideoPlayer::Invoke( IMFAsyncResult * pResult )
{
    IMFMediaEvent * pEvent = nullptr;

    // Get the event from the event queue.
    HRESULT hr = m_pSession->EndGetEvent( pResult, &pEvent );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Get the event type.
    MediaEventType meType;
    hr = pEvent->GetType( &meType );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    if ( meType == MESessionClosed )
    {
        // If the session is closed, the application is waiting on the event
        // handle. Also, do not request any more events from the session.

        SetEvent( m_hCloseEvent );
    }
    else
    {
        // For all other events, ask the media session for the
        // next event in the queue.
        hr = m_pSession->BeginGetEvent( this, nullptr );

        if ( FAILED( hr ) )
        {
            goto done;
        }
    }

    // For most events, post the event as a private window message to the
    // application. This lets the application process the event on its main
    // thread.

    // However, if a call to IMFMediaSession::Close is pending, it means the
    // application is waiting on the m_hCloseEvent event handle. (Blocking
    // call.) In that case, we simply discard the event.

    // When IMFMediaSession::Close is called, MESessionClosed is NOT
    // necessarily the next event that we will receive. We may receive any
    // number of other events before receiving MESessionClosed.

    if ( m_state != Closing )
    {
        // Leave a reference count on the event.
        pEvent->AddRef();
        PostMessage( m_hwndEvent, WM_PLAYEREVENT, ( WPARAM )pEvent, ( LPARAM )0 );
    }

done:
    SafeRelease( &pEvent );
    return S_OK;
}


//  Called by the application when it receives a WM_PLAYEREVENT
//  message.
//
//  This method is used to process media session events on the
//  application's main thread.
//
//  pUnkPtr: Pointer to the IUnknown interface of a media session
//  event (IMFMediaEvent).

HRESULT QVideoPlayer::HandleEvent( UINT_PTR pUnkPtr )
{
    HRESULT hrStatus = S_OK;            // Event status
    MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID;

    IUnknown * pUnk = nullptr;
    IMFMediaEvent * pEvent = nullptr;

    // pUnkPtr is really an IUnknown pointer.
    pUnk = ( IUnknown * )pUnkPtr;

    if ( pUnk == nullptr )
    {
        return E_POINTER;
    }

    HRESULT hr = pUnk->QueryInterface( IID_PPV_ARGS( &pEvent ) );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Get the event type.
    MediaEventType meType;
    hr = pEvent->GetType( &meType );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Get the event status. If the operation that triggered the event did
    // not succeed, the status is a failure code.
    hr = pEvent->GetStatus( &hrStatus );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Check if the async operation succeeded.
    if ( FAILED( hrStatus ) )
    {
        //QError e;
        //e.PrintMessage();

        hr = hrStatus;
        goto done;
    }

    // Switch on the event type. Update the internal state of the QVideoPlayer as needed.
    switch ( meType )
    {
            //unused
        case MESessionTopologySet:
            {

                break;
            }

        case MESessionTopologyStatus:
            // Get the status code.
            hr = pEvent->GetUINT32( MF_EVENT_TOPOLOGY_STATUS, ( UINT32 * )&TopoStatus );

            if ( FAILED( hr ) )
            {
                goto done;
            }

            switch ( TopoStatus )
            {
                case MF_TOPOSTATUS_READY:
                    hr = OnTopologyReady( pEvent );
                    break;

                default
                        :
                    // Nothing to do.
                    break;
            }

            break;

        case MEEndOfPresentation:
            hr = OnPresentationEnded( pEvent );
            break;

            ///audio extern volume
        case MEAudioSessionVolumeChanged:
            if ( m_pAudioVolum )
            {
                m_pAudioVolum->GetMasterVolume( &m_fLevel ); //trage volumul din afara inauntru
                m_pAudioVolum->GetMute( &m_bMute ); //trage mute status din afara inauntru
            }

            break;
    }

done:
    SafeRelease( &pUnk );
    SafeRelease( &pEvent );
    return hr;
}



///
/// All methods that follow are private to the QVideoPlayer class.
///


//  Handler for MESessionTopologyReady event.
//
//  - The MESessionTopologySet event means the session queued the
//    topology, but the topology is not ready yet. Generally, the
//    application does not need to respond to this event, unless
//    there is an error.
//  - The MESessionTopologyReady event means the new topology is
//    ready for playback. After this event is received, any calls to
//    IMFGetService will get service interfaces from the new topology.
//-------------------------------------------------------------------

HRESULT QVideoPlayer::OnTopologyReady( IMFMediaEvent * pEvent )
{
    SafeRelease( &m_pVideoDisplay );

    // Ask for the IMFVideoDisplayControl interface. This interface is
    // implemented by the EVR and is exposed by the media session as a service.

    // Note: This call is expected to fail if the source does not have video.

    HRESULT hr = MFGetService( m_pSession, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS( &m_pVideoDisplay ) );




    ///audio control
    hr = MFGetService( m_pSession, MR_POLICY_VOLUME_SERVICE,  IID_PPV_ARGS( &m_pAudioVolum ) );
    SetVolume( m_fLevel );
    SetMute( m_bMute );


    hr = StartPlayback();

    return hr;
}


//  Handler for MEEndOfPresentation event.

HRESULT QVideoPlayer::OnPresentationEnded( IMFMediaEvent * pEvent )
{
    // The session puts itself into the stopped state automatically.
    m_state = Stopped;

    return m_pSession->Stop();
}


//  Creates a new instance of the media session.

HRESULT QVideoPlayer::CreateSession()
{
    // Close the old session, if any.
    HRESULT hr = CloseSession();

    if ( FAILED( hr ) )
    {
        goto done;
    }

    ASSERT( m_state == Closed );

    // Create the media session.
    hr = MFCreateMediaSession( nullptr, &m_pSession );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    m_state = Ready;

    // Start pulling events from the media session
    hr = m_pSession->BeginGetEvent( ( IMFAsyncCallback * )this, nullptr );

    if ( FAILED( hr ) )
    {
        goto done;
    }


done:
    return hr;
}


//  Closes the media session.
//
//  The IMFMediaSession::Close method is asynchronous, but the CloseSession
//  method waits on the MESessionClosed event. The MESessionClosed event is
//  guaranteed to be the last event that the media session fires.

HRESULT QVideoPlayer::CloseSession()
{
    HRESULT hr = S_OK;

    SafeRelease( &m_pVideoDisplay );






    // First close the media session.
    if ( m_pSession )
    {
        DWORD dwWaitResult = 0;

        m_state = Closing;

        hr = m_pSession->Close();

        if ( FAILED( hr ) )
        {
            goto done;
        }

        // Wait for the close operation to complete

        WaitForSingleObject( m_hCloseEvent, 3000 );
        // Now there will be no more events from this session.
    }

    // Complete shutdown operations.

    // Shut down the media source. (Synchronous operation, no events.)
    if ( m_pSource )
    {
        m_pSource->Shutdown();
    }

    // Shut down the media session. (Synchronous operation, no events.)
    if ( m_pSession )
    {
        m_pSession->Shutdown();
    }

    SafeRelease( &m_pSource );
    SafeRelease( &m_pSession );





    ///audio volume
    SafeRelease( &m_pAudioVolum );

    m_state = Closed;

done:
    return hr;
}


//  Starts playback from the current position.

HRESULT QVideoPlayer::StartPlayback()
{
    ASSERT( m_pSession != nullptr );

    PROPVARIANT varStart;
    PropVariantInit( &varStart );

    varStart.vt = VT_EMPTY;

    HRESULT hr = m_pSession->Start( &GUID_NULL, &varStart );

    if ( SUCCEEDED( hr ) )
    {
        // Start is an asynchronous operation. However, we can treat our state
        // as being already started. If Start fails later, we'll get an
        // MESessionStarted event with an error code, and will update our state.
        m_state = Started;
    }

    PropVariantClear( &varStart );
    return hr;
}


//  Creates a media source from a URL.

HRESULT QVideoPlayer::CreateMediaSource( PCWSTR sURL )
{
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver * pSourceResolver = nullptr;
    IUnknown * pSource = nullptr;

    SafeRelease( &m_pSource );

    // Create the source resolver.
    HRESULT hr = MFCreateSourceResolver( &pSourceResolver );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Use the source resolver to create the media source.

    // Note: For simplicity this sample uses the synchronous method on
    // IMFSourceResolver to create the media source. However, creating a media
    // source can take a noticeable amount of time, especially for a network
    // source. For a more responsive UI, use the asynchronous
    // BeginCreateObjectFromURL method.

    hr = pSourceResolver->CreateObjectFromURL(
             sURL,                       // URL of the source.
             MF_RESOLUTION_MEDIASOURCE,  // Create a source object.
             nullptr,                       // Optional property store.
             &ObjectType,                // Receives the created object type.
             &pSource                    // Receives a pointer to the media source.
         );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Get the IMFMediaSource interface from the media source.
    hr = pSource->QueryInterface( IID_PPV_ARGS( &m_pSource ) );

done:
    SafeRelease( &pSourceResolver );
    SafeRelease( &pSource );
    return hr;
}



//  Creates a playback topology from the media source.
//
//  Pre-condition: The media source must be created already.
//                 Call CreateMediaSource() before calling this method.

HRESULT QVideoPlayer::CreateTopologyFromSource( IMFTopology ** ppTopology )
{
    ASSERT( m_pSession != nullptr );
    ASSERT( m_pSource != nullptr );

    IMFTopology * pTopology = nullptr;
    IMFPresentationDescriptor * pSourcePD = nullptr;
    DWORD cSourceStreams = 0;

    // Create a new topology.
    HRESULT hr = MFCreateTopology( &pTopology );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Create the presentation descriptor for the media source.
    hr = m_pSource->CreatePresentationDescriptor( &pSourcePD );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Get the number of streams in the media source.
    hr = pSourcePD->GetStreamDescriptorCount( &cSourceStreams );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // For each stream, create the topology nodes and add them to the topology.
    for ( DWORD i = 0; i < cSourceStreams; i++ )
    {
        hr = AddBranchToPartialTopology( pTopology, pSourcePD, i );

        if ( FAILED( hr ) )
        {
            goto done;
        }
    }

    // Return the IMFTopology pointer to the caller.
    *ppTopology = pTopology;
    ( *ppTopology )->AddRef();

done:
    SafeRelease( &pTopology );
    SafeRelease( &pSourcePD );
    return hr;
}


//  Adds a topology branch for one stream.
//
//  pTopology: Pointer to the topology object.
//  pSourcePD: The source's presentation descriptor.
//  iStream: Index of the stream to render.
//
//  Pre-conditions: The topology must be created already.
//
//  Notes: For each stream, we must do the following:
//    1. Create a source node associated with the stream.
//    2. Create an output node for the renderer.
//    3. Connect the two nodes.
//  The media session will resolve the topology, so we do not have
//  to worry about decoders or other transforms.

HRESULT QVideoPlayer::AddBranchToPartialTopology(
    IMFTopology * pTopology,
    IMFPresentationDescriptor * pSourcePD,
    DWORD iStream
)
{
    ASSERT( pTopology  );

    IMFStreamDescriptor * pSourceSD = nullptr;
    IMFTopologyNode * pSourceNode = nullptr;
    IMFTopologyNode * pOutputNode = nullptr;
    BOOL fSelected = FALSE;

    // Get the stream descriptor for this stream.
    HRESULT hr = pSourcePD->GetStreamDescriptorByIndex( iStream, &fSelected, &pSourceSD );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Create the topology branch only if the stream is selected.
    // Otherwise, do nothing.
    if ( fSelected )
    {
        // Create a source node for this stream.
        hr = CreateSourceStreamNode( m_pSource, pSourcePD, pSourceSD, &pSourceNode );

        if ( FAILED( hr ) )
        {
            goto done;
        }

        // Create the output node for the renderer.
        hr = CreateOutputNode( pSourceSD, m_hwndVideo, &pOutputNode );

        if ( FAILED( hr ) )
        {
            goto done;
        }

        // Add both nodes to the topology.
        hr = pTopology->AddNode( pSourceNode );

        if ( FAILED( hr ) )
        {
            goto done;
        }

        hr = pTopology->AddNode( pOutputNode );

        if ( FAILED( hr ) )
        {
            goto done;
        }

        // Connect the source node to the output node.
        hr = pSourceNode->ConnectOutput( 0, pOutputNode, 0 );
    }

done:
    // Clean up.
    SafeRelease( &pSourceSD );
    SafeRelease( &pSourceNode );
    SafeRelease( &pOutputNode );
    return hr;
}



//  Creates a source-stream node for a stream.
//
//  pSource: Pointer to the media source that contains the stream.
//  pSourcePD: Presentation descriptor for the media source.
//  pSourceSD: Stream descriptor for the stream.
//  ppNode: Receives a pointer to the new node.

HRESULT  CreateSourceStreamNode(
    IMFMediaSource * pSource,
    IMFPresentationDescriptor * pSourcePD,
    IMFStreamDescriptor * pSourceSD,
    IMFTopologyNode ** ppNode
)
{
    if ( !pSource || !pSourcePD || !pSourceSD || !ppNode )
    {
        return E_POINTER;
    }

    IMFTopologyNode * pNode = nullptr;

    // Create the source-stream node.
    HRESULT hr = MFCreateTopologyNode( MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Set attribute: Pointer to the media source.
    hr = pNode->SetUnknown( MF_TOPONODE_SOURCE, pSource );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Set attribute: Pointer to the presentation descriptor.
    hr = pNode->SetUnknown( MF_TOPONODE_PRESENTATION_DESCRIPTOR, pSourcePD );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Set attribute: Pointer to the stream descriptor.
    hr = pNode->SetUnknown( MF_TOPONODE_STREAM_DESCRIPTOR, pSourceSD );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Return the IMFTopologyNode pointer to the caller.
    *ppNode = pNode;
    ( *ppNode )->AddRef();

done:
    SafeRelease( &pNode );
    return hr;
}




//  Creates an output node for a stream.
//
//  pSourceSD: Stream descriptor for the stream.
//  ppNode: Receives a pointer to the new node.
//
//  Notes:
//  This function does the following:
//  1. Chooses a renderer based on the media type of the stream.
//  2. Creates an IActivate object for the renderer.
//  3. Creates an output topology node.
//  4. Sets the IActivate pointer on the node.

HRESULT CreateOutputNode(
    IMFStreamDescriptor * pSourceSD,
    HWND hwndVideo,
    IMFTopologyNode ** ppNode
)
{

    IMFTopologyNode * pNode = nullptr;
    IMFMediaTypeHandler * pHandler = nullptr;
    IMFActivate * pRendererActivate = nullptr;

    GUID guidMajorType = GUID_NULL;

    // Get the stream ID.
    DWORD streamID = 0;
    pSourceSD->GetStreamIdentifier( &streamID ); // Just for debugging, ignore any failures.

    // Get the media type handler for the stream.
    HRESULT hr = pSourceSD->GetMediaTypeHandler( &pHandler );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Get the major media type.
    hr = pHandler->GetMajorType( &guidMajorType );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Create a downstream node.
    hr = MFCreateTopologyNode( MF_TOPOLOGY_OUTPUT_NODE, &pNode );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Create an IMFActivate object for the renderer, based on the media type.
    if ( MFMediaType_Audio == guidMajorType )
    {
        // Create the audio renderer.
        hr = MFCreateAudioRendererActivate( &pRendererActivate );
    }
    else if ( MFMediaType_Video == guidMajorType )
    {
        // Create the video renderer.
        hr = MFCreateVideoRendererActivate( hwndVideo, &pRendererActivate );
    }
    else
    {
        hr = E_FAIL;
    }

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Set the IActivate object on the output node.
    hr = pNode->SetObject( pRendererActivate );

    if ( FAILED( hr ) )
    {
        goto done;
    }

    // Return the IMFTopologyNode pointer to the caller.
    *ppNode = pNode;
    ( *ppNode )->AddRef();

done:
    SafeRelease( &pNode );
    SafeRelease( &pHandler );
    SafeRelease( &pRendererActivate );
    return hr;
}


///Volume
/// 0.0 mute   -   1.0 maxim
//////////////////////////////////////////////////////////////////////////
HRESULT QVideoPlayer::SetVolume( float fLevel )
{
    HRESULT hr = S_OK;

    if ( m_pAudioVolum )
    {
        hr = m_pAudioVolum->SetMasterVolume( fLevel );

        if ( SUCCEEDED( hr ) )
        {
            m_fLevel = fLevel;
        }
    }
    else
    {
        // No volume control, just cache the value.
        m_fLevel = fLevel;
    }

    return hr;
}
HRESULT QVideoPlayer::GetVolume( float * pfLevel )
{
    if ( pfLevel == nullptr )
    {
        return E_POINTER;
    }

    *pfLevel = m_fLevel;
    return S_OK;
}

HRESULT QVideoPlayer::SetMute( BOOL bMute )
{
    HRESULT hr = S_OK;

    if ( m_pAudioVolum )
    {
        hr = m_pAudioVolum->SetMute( bMute );

        if ( SUCCEEDED( hr ) )
        {
            m_bMute = bMute;
        }
    }
    else
    {
        // No volume control, just cache the value.
        m_bMute = bMute;
    }

    return hr;
}
HRESULT QVideoPlayer::GetMute( BOOL * pbMute )
{
    if ( pbMute == nullptr )
    {
        return E_POINTER;
    }

    *pbMute = m_bMute;
    return S_OK;
}